/*
  * 生成 dist 文件夹，存放对应配置所有的 待启动项目
    1 解析配置，获取需要生成的文档地址
    2 生成 project 文件夹，存放对应配置所有的 待启动项目
*/
import fs, { ensureDirSync } from "fs-extra"
import path from "node:path"
import chalk from "chalk"
import { getDirName, removeDir, invokeWithErrorHandling, createFile, copyFile, copyDir, progress, dirIsExist, isImg, fileIsExist, renameDir, warn, success } from "./util.js"
import Creator from "./Creator.js"

type Options = Record<string, any>

const __dirname = getDirName()
const CONFIG_PATH = path.resolve(__dirname, "../config.json")
const PROJECT_PATH = path.resolve(__dirname, "../projects")
const IMGS_PATH = path.resolve(__dirname, "../imgs")
// template
const TEMPLATE_PATH = path.resolve(__dirname, "../template")
const TEMPLATE_PACKAGE_PATH = path.resolve(TEMPLATE_PATH, "./package.json")
const TEMPLATE_THEME_PATH = path.resolve(TEMPLATE_PATH, "./theme")
const TEMPLATE_INDEX_MD_PATH = path.resolve(TEMPLATE_PATH, "./index.md")
const TEMPLATE_SERVER_PATH = path.resolve(TEMPLATE_PATH, "./server")
// filter file
const FILTER = ["index.md", "assets"]
const WARN: string[] = []


export default async function build() {
  const creator = new Creator()
  creator.start()
  // // 1 获取配置
  // let options = fs.readJsonSync(CONFIG_PATH)
  // // 2 确认文件夹
  // fs.ensureDirSync(PROJECT_PATH)
  // // 3 遍历生成项目
  // Object.keys(options).forEach((key, index) => {
  //   invokeWithErrorHandling(createProject, [key, options[key]])
  // })
}

function createProject(name: string, options: Options) {
  const progressor = progress([
    `${chalk.yellow(name)}: start building...`,
    `${chalk.yellow(name)}: remove originDir and create new dir`,
    `${chalk.yellow(name)}: generate package.json`,
    `${chalk.yellow(name)}: create docs folder`,
    `${chalk.yellow(name)}: change docs folder name`,
    `${chalk.yellow(name)}: crate .vitepress, theme folder and index.ts file`,
    `${chalk.yellow(name)}: create public folder`,
    `${chalk.yellow(name)}: create index.md for every doc directory`,
    `${chalk.yellow(name)}: create index.md file`,
    `${chalk.yellow(name)}: create config.ts file`,
    `${chalk.yellow(name)}: create server folder`,
    `${chalk.yellow(name)}: ${chalk.blue('Create Success!')}:`,
  ])
  progressor.next()
  const ROOT = path.resolve(PROJECT_PATH, `./${name}`)
  const ROOT_PACKAGE_PATH = path.resolve(ROOT, './package.json')
  const ROOT_DOCS_PATH = path.resolve(ROOT, './docs')
  const ROOT_SERVER_PATH = path.resolve(ROOT, './server')
  const DOCS_PUBLIC_PATH = path.resolve(ROOT_DOCS_PATH, './public')
  const DOCS_INDEX_MD_PATH = path.resolve(ROOT_DOCS_PATH, './index.md')
  const DOCS_VITEPRESS_PATH = path.resolve(ROOT_DOCS_PATH, './.vitepress')
  const VITEPRESS_THEME_PATH = path.resolve(DOCS_VITEPRESS_PATH, './theme')
  const VITEPRESS_CONFIG_FILE_PATH = path.resolve(DOCS_VITEPRESS_PATH, './config.ts')
  const IS_NOT_FOLDER = options.sliders.unFolder    // docs 内部无目录，只有文件

  fs.ensureDir(PROJECT_PATH)

  // 1 创建文件夹
  progressor.next()
  removeDir(ROOT, ['node_modules']) && fs.ensureDirSync(ROOT)

  // 2 写入 packae.json
  progressor.next()
  if (options.port) {
    copyFile(TEMPLATE_PACKAGE_PATH, ROOT_PACKAGE_PATH, "", (data: string) => {
      let filter: Record<string, string> = {
        "/* NAME */": name,
        '/* PORT */': options.port,
      }
      Object.keys(filter).forEach(matchKey => {
        data = data.replaceAll(matchKey, filter[matchKey])
      })
      return data
    })
  } else {
    throw new Error(`unset <<< port >>> in ${name} project`)
  }

  // 3 生成 docs 文件夹
  progressor.next()
  fs.ensureDirSync(ROOT_DOCS_PATH)

  // 4 复制文档
  progressor.next()
  if (options.path) {
    const DOCS_FILES_PATH = path.resolve(__dirname, "../docs/前端修炼笔记/", options.path)
    if (!dirIsExist(DOCS_FILES_PATH)) {
      throw new Error(`create ${name} project path errror: ${DOCS_FILES_PATH} is unexist`)
    }
    if (!IS_NOT_FOLDER) {
      // @ts-ignore
      copyDir(DOCS_FILES_PATH, ROOT_DOCS_PATH, (data) => {
        return hanldeData(data)
      })
    } else {
      if (!options.sliders.en) throw new Error(`unset <<< sliders.en >>> in ${name} project`);
      ensureDirSync(`${ROOT_DOCS_PATH}/${options.sliders.en}`)
      // @ts-ignore
      copyDir(DOCS_FILES_PATH, `${ROOT_DOCS_PATH}/${options.sliders.en}`, (data) => {
        return hanldeData(data)
      })
    }
  } else {
    throw new Error(`unset <<< path >>> in ${name} project`)
  }

  // 5 修改 docs 中文件夹命名（不能是中文）
  progressor.next()
  if (!IS_NOT_FOLDER) {
    if (options.sliders) {
      let sliders = options.sliders
      Object.keys(sliders).forEach(folderName => {
        let val = sliders[folderName]
        if (!val.en) {
          throw new Error(`unset <<< ${folderName}.en >>> in ${name} project`)
        }
        let newPath = path.resolve(ROOT_DOCS_PATH, folderName)
        let oldPath = path.resolve(ROOT_DOCS_PATH, val.en)
        renameDir(newPath, oldPath)
      })
    } else {
      throw new Error(`unset <<< sliders >>> in ${name} project`)
    }
  }

  // 6 创建 .vitepress、theme 文件夹 和 index.ts 文件
  progressor.next()
  ensureDirSync(DOCS_VITEPRESS_PATH)
  createThemeDir(VITEPRESS_THEME_PATH, options)

  // 7 创建 public 文件夹，存放图片资源
  progressor.next()
  createPublicDir(DOCS_PUBLIC_PATH, name)

  // 8 为每一个 doc 目录创建 index.md
  createEveryDocIndexMd(ROOT_DOCS_PATH, options.sliders, IS_NOT_FOLDER)

  // 9 创建首页 index.md
  progressor.next()
  createIndexMd(DOCS_INDEX_MD_PATH, options)

  // 10 创建 config.ts
  progressor.next()
  createConfigFile(VITEPRESS_CONFIG_FILE_PATH, ROOT_DOCS_PATH, name, options, IS_NOT_FOLDER)

  // 11 生成 server 文件夹
  progressor.next()
  copyDir(TEMPLATE_SERVER_PATH, ROOT_SERVER_PATH)
  // 修改 .env 中的 PORT
  const data = fs.readFileSync(path.resolve(ROOT_SERVER_PATH, "./.env"))
  fs.writeFileSync(path.resolve(ROOT_SERVER_PATH, "./.env"), data + `${data.length != 0 && "\n"}PORT=${options.port || "4444"}`, { encoding: "utf-8" })
  copyFile(path.resolve(PROJECT_PATH, `./${name}`), DOCS_VITEPRESS_PATH, "VV.vue")

  // 11 输出完成结果
  progressor.next()
  console.log("");
  WARN.forEach(msg => warn(msg))
}


function hanldeData(data: string): string {
  // 1 获取默认配置
  const optionReg = /^\s*---([\w\W]+?)---/
  const optionMatch = data.match(optionReg) || []
  // 2 添加 outline: "deep" 显示二级标题
  let optionRes = `---\r\noutline: "deep"\r\n`
  if (optionMatch[0]) {
    data = data.replace(optionMatch[0], "")
    let inject = optionMatch[1]
    if (!inject.includes("outline")) {
      optionRes += inject
    }
  }
  optionRes += "---\r\n"
  data = `${optionRes}${data}`
  return data
}

function createThemeDir(dest: string, options: Options) {
  let filterArr = ["index.ts"]
  let replaceStr: Options = {
    "/* nav-style */": options["nav-style"] || `font-size: 14px;`,
    '/* img-style */': options["img-style"],
    '/* name-style */': options["hero"]["name-style"],
    '/* text-style */': options["hero"]["text-style"],
    '/* root-style */': options["root-style"],
    "/* github-show */": options.github ? "" : { display: 'none !important' }
  }
  copyDir(TEMPLATE_THEME_PATH, dest, (data, filename) => {
    if (filterArr.includes(filename)) {
      return data
    }
    else {
      for (const replaceKey in replaceStr) {
        let val = replaceStr[replaceKey]
        // 样式统一加个 ;
        let str = ""
        Object.keys(val).forEach(styleKey => {
          str += `${styleKey}: ${val[styleKey]};`
        })
        data = data.replaceAll(replaceKey, str)
      }
    }
    return data
  })
}

function createPublicDir(path: string, name: string) {
  fs.ensureDirSync(path)
  let imgs = fs.readdirSync(IMGS_PATH)
  let matchKey = new RegExp(`(${name})|(${name.toLocaleLowerCase()})`, "g")
  let hasFavicon = false
  for (const img of imgs) {
    if (matchKey.test(img)) {
      copyFile(`${IMGS_PATH}/${img}`, `${path}/favicon.svg`, "")
      hasFavicon = true
      break;
    }
  }
  if (!hasFavicon) {
    throw new Error(`unset <<< favicon.svg >>> in ${name} project, please check your imgs`)
  }
}


function createEveryDocIndexMd(docsPath: string, sliders: Options, isNotFolder: boolean) {
  if (!isNotFolder) {
    Object.keys(sliders).forEach(sliderKey => {
      let slider = sliders[sliderKey]
      let folderName = slider.en
      let destPath = path.resolve(docsPath, folderName)
      let start = slider.start || ""
      let end = slider.end || ""
      let str = ""
      let files = fs.readdirSync(destPath)
      files.forEach((fileName, index) => {
        if (FILTER.includes(fileName)) {
          // 内部已经有 index.md 给出警告
          if (fileName === "index.md") {
            WARN.push(`There is a index.md in ${folderName} at ${docsPath}`)
          }
          return
        }
        str += `[${fileName.replace(/.md$/, '')}](/${folderName}/${fileName})<br />`
      })
      str = `${start}\n\n${str}\n\n${end}`
      createFile(`${destPath}/index.md`, str)
    })
  } else {
    let slider = sliders
    let folderName = slider.en
    let destPath = path.resolve(docsPath, folderName)
    let start = slider.start || ""
    let end = slider.end || ""
    let str = ""
    let files = fs.readdirSync(destPath)
    files.forEach((fileName, index) => {
      if (FILTER.includes(fileName)) {
        // 内部已经有 index.md 给出警告
        if (fileName === "index.md") {
          WARN.push(`There is a index.md in ${folderName} at ${docsPath}`)
        }
        return
      }
      str += `[${fileName.replace(/.md$/, '')}](/${folderName}/${fileName})<br />`
    })
    str = `${start}\n\n${str}\n\n${end}`
    createFile(`${destPath}/index.md`, str)
  }
}


function createConfigFile(destPath: string, docsPath: string, title: string, options: Options, isNotFolder: boolean) {
  if (!options.sliders) {
    throw new Error(`unset <<< sliders >>> in ${title} project`)
  }

  let nav = getNav(options.sliders, options["other-nav"], isNotFolder)
  let footer = {
    message: "Released under the MulanPSL-1.0 License.",
    copyright: `Copyright © ${new Date().getFullYear()}-present sishen`,
  }
  let editLink = {
    pattern: "https://gitee.com/ma_zipeng/TS-Note/tree/master/docs/:path",
    text: "Edit this page on Gitee",
  }
  let sidebar = getSidebar(options.sliders, docsPath, isNotFolder)

  const congfig = {
    title,
    titleTemplate: false,
    head: [["link", { rel: "icon", href: "/favicon.svg" }]],
    lastUpdated: true,
    lang: "en-US",
    themeConfig: {
      siteTitle: "TypeScript",
      logo: "/favicon.svg",
      socialLinks: [
        { icon: "github", link: options.github },
      ],
      nav, footer, sidebar
    }
  }

  createFile(destPath, `export default ${JSON.stringify(congfig)}`)
}
function getNav(sliders: Options, otherNav: Options, isNotFolder: boolean) {
  let nav: Options[] = []
  if (!isNotFolder) {
    Object.keys(sliders).forEach(key => {
      let val = sliders[key]
      let text = key.replace(/^\d+-/, "")
      let link = `/${val.en}/`
      nav.push({ text, link })
    })
  } else {
    let val = sliders
    let text = val.sliderName
    let link = `/${val.en}/`
    nav.push({ text, link })
  }
  if (otherNav) {
    Object.keys(otherNav).forEach(key => {
      let val = otherNav[key]
      let text = key.replace(/^\d+-/, "")
      if (Array.isArray(val)) {
        nav.push({ text, items: val })
      } else {
        nav.push({ text, link: val })
      }
    })
  }
  return nav
}
function getSidebar(sliders: Options, docsPath: string, isNotFolder: boolean) {
  let sidebar: Options = {}
  if (!isNotFolder) {
    Object.keys(sliders).forEach(sliderKey => {
      let val = sliders[sliderKey]
      let key = `/${val.en}/`
      let files = fs.readdirSync(path.resolve(docsPath, val.en))
      let items: Options[] = []
      files.forEach(file => {
        if (FILTER.includes(file)) return;
        items.push({
          text: file.replace(/\.md$/, ""),
          link: `${key}${file}`
        })
      })
      sidebar[key] = [{
        text: val.en,
        items: items,
        collapsible: true
      }]
    })
  } else {
    let val = sliders
    let key = `/${val.en}/`
    let files = fs.readdirSync(path.resolve(docsPath, val.en))
    let items: Options[] = []
    files.forEach(file => {
      if (FILTER.includes(file)) return;
      items.push({
        text: file.replace(/\.md$/, ""),
        link: `${key}${file}`
      })
    })
    sidebar[key] = [{
      text: val.en,
      items: items,
      collapsible: true
    }]
  }
  return sidebar
}


function createIndexMd(destPath: string, options: Options) {
  let replaceStr: Options = {
    "#name": options.hero.name,
    '#text': options.hero.text,
    '#tagline': options.hero.tagline,
    '#actions': getActions(options.hero.actions),
    '#features': getFeatures(options.features)
  }
  copyFile(TEMPLATE_INDEX_MD_PATH, destPath, "", data => {
    for (const replaceKey in replaceStr) {
      let val = replaceStr[replaceKey]
      data = data.replaceAll(replaceKey, val)
    }
    return data
  })
}
function getActions(actions: Options[]) {
  let res = ""
  actions.forEach((v, i) => {
    if (i === 0) {
      res += `- theme: ${v.theme}
      text: ${v.text}
      link: ${v.link}\n`
    }
    else {
      res += `    - theme: ${v.theme}
      text: ${v.text}
      link: ${v.link}\n`
    }
  })
  return res
}
function getFeatures(features: Options[]) {
  let res = ""
  features.forEach((v, i) => {
    if (i === 0) {
      res += `- icon: ${v.icon}
    title: ${v.title}
    details: ${v.details}\n`
    }
    else {
      res += `  - icon: ${v.icon}
    title: ${v.title}
    details: ${v.details}\n`
    }
  })
  return res
}
